//
// Created by shaowenhan on 2020/6/7.
//

#ifndef COMPOSITENEIGHBOR_COSTANDPENALTY_H
#define COMPOSITENEIGHBOR_COSTANDPENALTY_H

#include "Gain.h"

class CostAndPenalty{
private:
    bool arePenaltiesEqual(const DisType anotherPenalties[Constraint::num]) const
    {
        for(int i = 0; i < Constraint::num; i++)
        {
            if(not isEqual(penalties[i], anotherPenalties[i]))
                return false;
        }
        return true;
    }
public:
    DisType totalCost;
    DisType penalties[Constraint::num];

    explicit CostAndPenalty() = default;
    // construct an object of CostAndPenalty from a feasible solution with given totalCost
    explicit CostAndPenalty(DisType totalCost):totalCost(totalCost){
        for(auto & penalty : penalties)
            penalty = 0;
    }
    CostAndPenalty(const CostAndPenalty& costAndPenalty){
        totalCost = costAndPenalty.totalCost;
        for(int i = 0; i < Constraint::num; i++)
            penalties[i] = costAndPenalty.penalties[i];
    }

    Gain operator-(const CostAndPenalty& costAndPenalty) const
    {
        Gain res;
        res.costGain = totalCost-costAndPenalty.totalCost;
        for(int i = 0; i < Constraint::num; i++)
            res.penaltyGains[i] = penalties[i]-costAndPenalty.penalties[i];
        return res;
    }
    CostAndPenalty& operator-=(const Gain& gain)
    {
        totalCost -= gain.costGain;
        for(int i = 0; i < Constraint::num; i++)
            penalties[i] -= gain.penaltyGains[i];
        return *this;
    }
    CostAndPenalty operator-(const Gain& gain) const
    {
        CostAndPenalty res{*this};
        res.totalCost -= gain.costGain;
        for(int i = 0; i < Constraint::num; i++)
            res.penalties[i] -= gain.penaltyGains[i];
        return res;
    }
    bool operator==(const CostAndPenalty& costAndPenalty) const
    {
        return isEqual(totalCost, costAndPenalty.totalCost) and
               arePenaltiesEqual(costAndPenalty.penalties);
    }
    CostAndPenalty& operator=(const CostAndPenalty &costAndPenalty)
    {
        if(&costAndPenalty != this)
        {
            totalCost = costAndPenalty.totalCost;
            for(int i = 0; i < Constraint::num; i++)
                penalties[i] = costAndPenalty.penalties[i];
        }
        return *this;
    }
    [[deprecated("It is used when debugging")]]
    bool isLegalCostAndPenalty() const
    {
        for(auto penalty : penalties)
        {
            if(isFirstLessThanSecond(penalty, 0))
                return false;
        }
        return isFirstGreaterThanOrEqualToSecond(totalCost, 0);
    }
    bool isConstraintLegal(Constraint constraint) const{
        return isZero(penalties[constraint]);
    }
    bool isFeasible() const
    {
        for(auto penalty : penalties)
        {
            if(not isZero(penalty))
                return false;
        }
        return true;
    }
};

#endif //COMPOSITENEIGHBOR_COSTANDPENALTY_H
